package com.sh.data.engine.domain.normalization.modeldesign.model.service.impl;

import com.google.common.collect.Lists;
import com.sh.data.engine.common.enumDefinition.DSType;
import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.annotation.StorageCheckAnnotation;
import com.sh.data.engine.domain.base.model.PageResult;
import com.sh.data.engine.domain.base.model.UserContext;
import com.sh.data.engine.domain.base.model.enums.Database;
import com.sh.data.engine.domain.dataasset.model.domain.TableInfoBizDomain;
import com.sh.data.engine.domain.dataasset.model.param.QueryTableParam;
import com.sh.data.engine.domain.dataasset.model.param.SaveNewTblPropParam;
import com.sh.data.engine.domain.dataasset.service.TableService;
import com.sh.data.engine.domain.dataasset.service.TblPropService;
import com.sh.data.engine.domain.normalization.modeldesign.busmatrix.service.BusMatrixService;
import com.sh.data.engine.domain.normalization.modeldesign.cdm.service.TblRelationService;
import com.sh.data.engine.domain.normalization.modeldesign.dimfact.model.domain.ModelBindWarnDomain;
import com.sh.data.engine.domain.normalization.modeldesign.dimfact.service.DimFactService;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.domain.*;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.enums.*;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.mapper.SaveModelArgMapper;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.param.ExternalSaveModelParam;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.param.SaveFieldParam;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.param.SaveModelParam;
import com.sh.data.engine.domain.normalization.modeldesign.model.service.ModelService;
import com.sh.data.engine.domain.normalization.research.themedesign.model.domain.ThemeDomain;
import com.sh.data.engine.domain.normalization.research.themedesign.service.ThemeService;
import com.sh.data.engine.domain.normalization.standard.model.domain.DictionaryDomain;
import com.sh.data.engine.domain.normalization.standard.service.IDictionaryService;
import com.sh.data.engine.domain.shims.db.BaseDbManager;
import com.sh.data.engine.domain.shims.db.model.TableInfoDomain;
import com.sh.data.engine.domain.util.ConvertUtil;
import com.sh.data.engine.domain.workspace.index.service.ProjectService;
import com.sh.data.engine.domain.workspace.manager.service.impl.StorageServiceImpl;
import com.sh.data.engine.repository.dao.dataasset.entity.TblPropEntity;
import com.sh.data.engine.repository.dao.normalization.modeldesign.cdm.entity.TblRelationEntity;
import com.sh.data.engine.repository.dao.normalization.modeldesign.model.arg.SaveModelArg;
import com.sh.data.engine.repository.dao.workspace.manager.entity.StorageEntity;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.apache.commons.lang3.StringUtils.isEmpty;

/**
 * @author : changkong
 * @date : 2022/1/17
 */
@Slf4j
@Service
public class ModelServiceImpl implements ModelService {

    @Autowired
    private TblPropService tblPropService;

    @Lazy
    @Autowired
    private DimFactService dimFactService;

    @Autowired
    private ProjectService projectService;

    @Autowired
    private StorageServiceImpl storageServiceImpl;

//    @Autowired
//    private UserService userService;

    @Autowired
    private ThemeService themeService;

    @Autowired
    private BusMatrixService busMatrixService;

    @Autowired
    private TableService tableService;

    @Autowired
    private IDictionaryService dictionaryService;

    @Autowired
    private TblRelationService tblRelationService;

    @Autowired
    private SaveModelArgMapper saveModelArgMapper;

    /**
     * 获取数据标准
     *
     * @param projectId
     * @return
     */
    @Override
    public List<DictionaryDomain> getDictionaryList(Long projectId) {
        return dictionaryService.getPublishedDictionaries(projectId);
    }

    /**
     * 数据库中查所有表
     *
     * @param pageNum
     * @param pageSize
     * @param modelType ods, public, ads, other
     * @param search
     * @return
     */
    @StorageCheckAnnotation
    @Override
    public PageResult<ModelListDomain> getTablePage(
        Integer pageNum, Integer pageSize, List<String> modelType, String search) {
        QueryTableParam queryTableParam = new QueryTableParam();
        queryTableParam.setPageNum(pageNum);
        queryTableParam.setPageSize(pageSize);
        queryTableParam.setSearchStr(search);
        queryTableParam.setSortField("UPDATETIME");
        if (modelType.contains(ModelTypeEnum.TMP.getName())) {
            queryTableParam.setOrder("desc");
            queryTableParam.setTableNameReversePrefixList(
                Lists.newArrayList(
                    ModelTypeEnum.ODS.getName(),
                    ModelTypeEnum.DIM.getName(),
                    ModelTypeEnum.DWD.getName(),
                    ModelTypeEnum.DWS.getName(),
                    ModelTypeEnum.ADS.getName()));
        } else {
            queryTableParam.setTableNamePrefixList(modelType);
        }

        var projectId = ServletUtils.getProjectId();
        PageResult<TableInfoBizDomain> tablePage =
            tableService.getTablePage(projectId, queryTableParam);

        // TODO

        if (isEmptyTablePage(tablePage)) {
            return new PageResult<>(Lists.newArrayList(), pageNum, pageSize, 0);
        }

        return getModelListDomainPageResult(tablePage);
    }

    /**
     * 数据库中查所有表-不分页
     *
     * @param modelType ods, public, ads, other
     * @param search
     * @return
     */
    @StorageCheckAnnotation
    @Override
    public List<ModelListDomain> getTableList(List<String> modelType, String search, Long themeId) {
        Long projectId = ServletUtils.getProjectId();
        List<ThemeDomain> themeDomains = new ArrayList<>();
        // 主题列表为空也需要展示出其他地方创建的模型
        if (null != themeId) {
            themeDomains = themeService.treeThemesByPid(projectId, themeId);
        }
        List<Long> themes = themeDomains.stream().map(ThemeDomain::getId).collect(Collectors.toList());

        QueryTableParam queryTableParam = new QueryTableParam();
        queryTableParam.setThemeIdList(themes);
        queryTableParam.setSearchStr(search);
        queryTableParam.setSortField("UPDATETIME");
        if (modelType.contains(ModelTypeEnum.TMP.getName())) {
            queryTableParam.setOrder("desc");
            queryTableParam.setTableNameReversePrefixList(
                Lists.newArrayList(
                    ModelTypeEnum.ODS.getName(),
                    ModelTypeEnum.DIM.getName(),
                    ModelTypeEnum.DWD.getName(),
                    ModelTypeEnum.DWS.getName(),
                    ModelTypeEnum.ADS.getName()));
        } else {
            queryTableParam.setTableNamePrefixList(modelType);
        }
        List<TableInfoBizDomain> tableList =
            tableService.getTableList(projectId, queryTableParam);

        return getModelListDomainResult(tableList);
    }

    /**
     * 按类型获取模型分页列表
     *
     * @param pageNum
     * @param pageSize
     * @param themeId
     * @param namesByType 小分类: ods, dwd, dws, dim, ads, tmp
     * @return
     */
    @StorageCheckAnnotation
    @Override
    public PageResult<ModelListDomain> getTablePageByThemeId(
        Integer pageNum, Integer pageSize, Long themeId, List<String> namesByType, String search) {
        Long projectId = ServletUtils.getProjectId();

        List<ThemeDomain> themeDomains = new ArrayList<>();
        // 主题列表为空也需要展示出其他地方创建的模型
        if (null != themeId) {
            themeDomains = themeService.treeThemesByPid(projectId, themeId);
        }

        List<Long> themes = themeDomains.stream().map(ThemeDomain::getId).collect(Collectors.toList());
        log.info(
            "模型列表查询参数: pageNum={}, pageSize={}, themeId={}, namesByType={}",
            pageNum,
            pageSize,
            themeId,
            namesByType);

        QueryTableParam queryTableParam = new QueryTableParam();
        queryTableParam.setSortField("UPDATETIME");
        queryTableParam.setOrder("desc");
        queryTableParam.setPageNum(pageNum);
        queryTableParam.setPageSize(pageSize);
        queryTableParam.setSearchStr(search);
        queryTableParam.setThemeIdList(themes);
        queryTableParam.setTableNamePrefixList(namesByType);
        PageResult<TableInfoBizDomain> tablePage =
            tableService.getTablePage(projectId, queryTableParam);

        if (isEmptyTablePage(tablePage)) {
            return new PageResult<>(Lists.newArrayList(), pageNum, pageSize, 0);
        }

        return getModelListDomainPageResult(tablePage);
    }

    /**
     * 按id查询简化的模型信息, 不包含字段列表
     *
     * @param id
     * @return
     */
    @Override
    public ModelDomain getSimpifyModelById(Long projectId, Long id) {
        log.info("查询模型详情参数: projectId={}, id={}", projectId, id);
        TblPropEntity tblPropEntity = tblPropService.getTblPropById(id);
        if (null == tblPropEntity) {
            log.error("检查模型: 关联table: {}", id);
            throw new BusinessException("检查tblProp中表状态");
        }
        ModelDomain modelDomain = new ModelDomain();
        modelDomain.setId(tblPropEntity.getId());
        modelDomain.setCnName(tblPropEntity.getTableComment());
        modelDomain.setCreateBy(tblPropEntity.getCreatorId());
        modelDomain.setUpdateTime(tblPropEntity.getUpdateTime());
        modelDomain.setCreateTime(tblPropEntity.getCreateTime());
        modelDomain.setSchema(tblPropEntity.getSchema());
        modelDomain.setEnName(tblPropEntity.getTableName());
        modelDomain.setLifeCycle(tblPropEntity.getLifecycle());
        modelDomain.setThemeId(tblPropEntity.getThemeId());
        return modelDomain;
    }

    /**
     * 按id查询模型信息, 并且去数据源查询字段列表
     *
     * @param id
     * @return
     */
    @Override
    public ModelDomain getModelById(Long projectId, Long id) {
        log.info("查询模型详情参数: projectId={}, id={}", projectId, id);
        TblPropEntity tblPropEntity = tblPropService.getTblPropById(id);
        if (null == tblPropEntity) {
            throw new BusinessException("检查模型参数");
        }

        return convertTblEntityToModelDomain(projectId, tblPropEntity);
    }

    /**
     * 只返回模型Id
     *
     * @param projectId
     * @param tblId
     * @return
     */
    @Override
    public ModelDomain getModelIdByTblId(Long projectId, long tblId) {
        log.info("查询模型详情参数: projectId={}, tblId={}", projectId, tblId);
        TblPropEntity tblPropEntity = tblPropService.getTblPropByTblId(projectId, tblId);
        if (null == tblPropEntity) {
            throw new BusinessException("检查模型参数");
        }
        if (0 == tblPropEntity.getRowState()) {
            throw new BusinessException("检查该表在虎符中的状态");
        }
        return ModelDomain.builder().id(tblPropEntity.getId()).build();
    }

    /**
     * 按tblId查询模型信息, 并且去数据源查询字段列表 Sync任务是批量插入表, 插入后没有更新id, 所以返回给页面接口中的id为空, 可以>通过调这个方法获取详情
     *
     * @param projectId
     * @param tblId
     * @return
     */
    @StorageCheckAnnotation
    @Override
    public ModelDomain getModelByTblId(Long projectId, long tblId) {
        log.info("查询模型详情参数: projectId={}, tblId={}", projectId, tblId);
        TblPropEntity tblPropEntity = tblPropService.getTblPropByTblId(projectId, tblId);
        if (null == tblPropEntity) {
            throw new BusinessException("检查模型参数");
        }
        if (0 == tblPropEntity.getRowState()) {
            throw new BusinessException("检查该表在虎符中的状态");
        }
        return convertTblEntityToModelDomain(projectId, tblPropEntity);
    }

    @NotNull
    private ModelDomain convertTblEntityToModelDomain(Long projectId, TblPropEntity tblPropEntity) {
        ModelDomain modelDomain = new ModelDomain();
        modelDomain.setId(tblPropEntity.getId());
        modelDomain.setCnName(tblPropEntity.getTableComment());
        modelDomain.setSchema(tblPropEntity.getSchema());
        modelDomain.setCreateBy(tblPropEntity.getCreatorId());
        modelDomain.setUpdateTime(tblPropEntity.getUpdateTime());
        modelDomain.setCreateTime(tblPropEntity.getCreateTime());
        modelDomain.setEnName(tblPropEntity.getTableName());
        modelDomain.setLifeCycle(tblPropEntity.getLifecycle());
        modelDomain.setThemeId(tblPropEntity.getThemeId());
        modelDomain.setDataSecretId(tblPropEntity.getDataSecretId());
        BaseDbManager dbManager = null;
        try {
            dbManager = projectService.getDbManager(projectId);
            StorageEntity storageEntity = storageServiceImpl.tmpGetStorageInfoByProjectId(projectId);
            // 根据存储类型获取对应的表操作
            DSType dsType = DSType.from(storageEntity.getStorageType());
            TableOperation tableOperation = new TableOperation(dsType);

            // 返回给前端判断是否展示分区|必填|富文本
            modelDomain.setHive(DSType.Hive == dsType);

            String schemaName = tblPropEntity.getSchema();
            if (isEmpty(modelDomain.getCnName())) {
                TableInfoDomain tableInfoDomain =
                    tableOperation.getTableInfoDomain(
                        storageEntity.getRmdbs().getDbName(),
                        schemaName,
                        tblPropEntity.getTableName(),
                        dbManager);
                modelDomain.setCnName(null != tableInfoDomain ? tableInfoDomain.getTableComment() : "");
            }

            // 从meatadata中取
            List<ModelDomain.Field> tableField =
                tableOperation.getTableField(
                    storageEntity.getRmdbs().getDbName(),
                    schemaName,
                    tblPropEntity.getTableName(),
                    dbManager);
            // 关闭连接
            modelDomain.setFields(tableField);
            return modelDomain;
        } finally {
            closeDbManager(dbManager);
        }
    }

    /**
     * @param schema
     * @param tableName
     * @retur
     */
    @StorageCheckAnnotation
    @Override
    public ModelDomain getModelByName(Long projectId, String schema, String tableName) {
        log.info("查询模型详情参数: projectId={}, schema={}, tableName={}", projectId, schema, tableName);

        StorageEntity storageEntity = storageServiceImpl.tmpGetStorageInfoByProjectId(projectId);
        // 根据存储类型获取对应的表操作
        DSType dsType = DSType.from(storageEntity.getStorageType());
        TableOperation tableOperation = new TableOperation(dsType);
        final BaseDbManager dbManager = projectService.getDbManager(projectId);
        TblPropEntity tblPropEntity =
            tblPropService.getTblPropBySchemaTblName(projectId, schema, tableName);
        if (null == tblPropEntity) {
            log.info("该表还未同步到虎符中");
        }
        if (null != tblPropEntity && 0 == tblPropEntity.getRowState()) {
            log.info("检查该表在虎符中的状态");
        }

        //    String schemaName = storageEntity.getRmdbs().getSchema();
        TableInfoDomain tableInfoDomain =
            tableOperation.getTableInfoDomain(
                storageEntity.getRmdbs().getDbName(), schema, tableName, dbManager);

        ModelDomain modelDomain = new ModelDomain();
        modelDomain.setId(null != tblPropEntity ? tblPropEntity.getId() : null);
        modelDomain.setCnName(tableInfoDomain.getTableComment());
        //    modelDomain.setCreateBy(tableInfoDomain.getCreatorId());
        modelDomain.setUpdateTime(
            null != tableInfoDomain.getLastDdlTime()
                ? new Date(tableInfoDomain.getLastDdlTime())
                : null);
        //    modelDomain.setCreateTime(tblPropEntity.getCreateTime());
        modelDomain.setEnName(tableInfoDomain.getTableName());
        modelDomain.setLifeCycle(-1);
        modelDomain.setThemeId(null != tblPropEntity ? tblPropEntity.getThemeId() : null);
        // 返回给前端判断是否展示分区|必填|富文本
        modelDomain.setHive(DSType.Hive == dsType);

        // 从meatadata中取
        List<ModelDomain.Field> tableField =
            tableOperation.getTableField(
                storageEntity.getRmdbs().getDbName(), schema, tableName, dbManager);
        // 关闭连接
        closeDbManager(dbManager);
        modelDomain.setFields(tableField);

        return modelDomain;
    }

    /**
     * 新增模型
     *
     * @param saveNewModelParam
     * @return
     */
    @StorageCheckAnnotation
    @Override
    public boolean saveNewModel(SaveModelParam saveNewModelParam) {
        if (!saveNewModelParam.isIgnoreTheme()) {
            // 顶层主题禁止创建模型
            Long themeId = saveNewModelParam.getThemeId();
            ThemeDomain themeById = themeService.getThemeById(themeId);
            if (null == themeById || Objects.equals(0L, themeById.getPid())) {
                throw new BusinessException("顶层主题禁止创建模型");
            }
        }
        // 保存到tblProperties表
        //    SaveNewTblPropParam saveNewTblPropParam = new SaveNewTblPropParam();

        StorageEntity storageEntity =
            storageServiceImpl.tmpGetStorageInfoByProjectId(saveNewModelParam.getProjectId());

        if (null == storageEntity) {
            throw new BusinessException("检查数据库配置");
        }

        DSType dsType = DSType.from(storageEntity.getStorageType());

        String dbName = storageEntity.getRmdbs().getDbName();

        // oracle, pg use rmdbs schema
        String schemaName = storageEntity.getRmdbs().getSchema();
        // sap use request parameter schema
        if (DSType.Sap == dsType) {
            schemaName = saveNewModelParam.getSchema();
        }

        String tableName = saveNewModelParam.getEnName();
        if (DSType.Hive == dsType) {
            if (!isAllAlphaLowerCase(tableName)) {
                throw new BusinessException("hive表名必须小写");
            }
        }

        TableOperation tableOperation = new TableOperation(dsType);

        var projectId = ServletUtils.getProjectId();
        TblPropEntity existTableName =
            tblPropService.getTblPropByTblName(projectId, schemaName, tableName);
        if (null != existTableName && existTableName.getRowState() == 1) {
            log.info("mongodb中表{}已存在", tableName);
            throw new BusinessException("表名重复");
        }

        // 使用后记得关闭, HiveDbManager持有HiveMetaStoreClient连接
        BaseDbManager dbManager = projectService.getDbManager(saveNewModelParam.getProjectId());

        // 检查数据源中表是否存在
        boolean tableNameExist =
            tableOperation.isTableNameExist(dbName, schemaName, tableName, dbManager);
        if (tableNameExist) {
            closeDbManager(dbManager);
            throw new BusinessException("英文名重复");
        }
        // 建表操作
        CreateTableDomain createTableDomain =
            CreateTableDomain.builder()
                .dbName(dbName)
                .schemaName(schemaName)
                .tableName(tableName)
                .tableComment(saveNewModelParam.getCnName())
                .fieldList(saveNewModelParam.getFields())
                .dbManager(dbManager)
                .build();
        try {
            boolean createTableResult = tableOperation.createTable(createTableDomain);
            if (!createTableResult) {
                throw new BusinessException("检查底层数据源状态");
            }
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        } finally {
            closeDbManager(dbManager);
        }
        SaveNewTblPropParam saveNewTblPropParam = new SaveNewTblPropParam();
        if (DSType.hasSchema(storageEntity.getStorageType())) {
            saveNewTblPropParam.setSchema(storageEntity.getRmdbs().getSchema());
            // sap use request parameter schema
            if (DSType.Sap == dsType) {
                saveNewTblPropParam.setSchema(schemaName);
            }
            if (DSType.DM == dsType) {
                saveNewTblPropParam.setSchema(storageEntity.getRmdbs().getDbName());
            }
        }
        saveNewTblPropParam.setProjectId(projectId);
        saveNewTblPropParam.setTableComment(saveNewModelParam.getCnName());
        saveNewTblPropParam.setThemeId(saveNewModelParam.getThemeId());
        saveNewTblPropParam.setTableName(tableName);
        saveNewTblPropParam.setTblId(tblPropService.generateTblId(projectId));
        // default managed_table
        saveNewTblPropParam.setTblType(TableInfoDomain.TblType.MANAGED_TABLE.name());
        saveNewTblPropParam.setLifeCycle(saveNewModelParam.getLifeCycle());
        saveNewTblPropParam.setDataSecretId(saveNewModelParam.getDataSecretId());
        String userId = UserContext.getUserId();
        saveNewTblPropParam.setCreatorId(userId);
        TblPropEntity tblPropEntity = tblPropService.saveNewTblPropEntity(saveNewTblPropParam);
        // biz data index
        saveNewModelParam.setModelId(tblPropEntity.getId());
        return null == tblPropEntity;
    }

    /**
     * 修改模型
     *
     * @param saveModelParam
     */
    @StorageCheckAnnotation
    @Override
    public void saveModel(SaveModelParam saveModelParam) {
        Long modelId = saveModelParam.getModelId();
        var projectId = ServletUtils.getProjectId();

        StorageEntity storageEntity = storageServiceImpl.getStorageInfoByProjectId(projectId);
        if (null == storageEntity) {
            throw new BusinessException("检查底层存储配置是否异常");
        }

        TblPropEntity tblPropEntity = tblPropService.getTblPropById(modelId);
        if (null == tblPropEntity) {
            log.error("检查模型: {}  关联table异常", modelId);
            throw new BusinessException("检查数据库tbl_prop状态是否异常");
        }

        DSType dsType = DSType.from(storageEntity.getStorageType());

        if (DSType.Hive != dsType && saveModelParam.isSupportRtf()) {
            log.error("非hive数据源禁止设置富文本");
            throw new BusinessException("非hive数据源禁止设置富文本");
        }

        // sap disable change schema
        if (DSType.Sap == dsType) {
            if (!Objects.equals(tblPropEntity.getSchema(), saveModelParam.getSchema())) {
                throw new BusinessException("sap数据库禁止切换模型schema");
            }
        }

        ModelDomain dbModel = convertTblPropEntityToModelDomain(projectId, tblPropEntity);

        TableOperation tableOperation = new TableOperation(dsType);

        String dbName = storageEntity.getRmdbs().getDbName();

        // use tbl_prop schema
        String schema = tblPropEntity.getSchema();

        // 进入各自的编辑操作
        BaseDbManager dbManager = projectService.getDbManager(projectId);

        final TblPropEntity originTblEntityCopy = ConvertUtil.copyProperties(tblPropEntity, TblPropEntity.class);
        // 更新mongo数据库内容
        updateTblProp(saveModelParam, dbModel, tblPropEntity);
        try {
            boolean editTableResult =
                tableOperation.editFormTable(dbName, schema, dbModel, saveModelParam, dbManager);
            if (!editTableResult) {
                // restore mongo
                tblPropService.updateTblPropById(originTblEntityCopy);
                throw new BusinessException("修改表失败, 检查模型参数");
            }
            saveModelParam.setTblId(tblPropEntity.getTblId());
            tblRelationService.refreshTblRelation(saveModelParam);
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        } finally {
            closeDbManager(dbManager);
        }
    }

    /**
     * 前端创建模型时需要知道存储类型
     *
     * @param projectId
     * @return
     */
    @Override
    public StorageDomain getStorageType(Long projectId) {
        StorageEntity storageEntity = storageServiceImpl.tmpGetStorageInfoByProjectId(projectId);
        if (null == storageEntity) {
            throw new BusinessException("该项目下没有存储信息");
        }
        String storageType = storageEntity.getStorageType();
        StorageDomain storageDomain = new StorageDomain();
        storageDomain.setType(storageType);
        switch (storageType) {
            case "Hive":
                storageDomain.setFieldTypes(HiveFieldTypeEnum.getAllFieldType());
                break;
            case "PostgreSQL":
                storageDomain.setFieldTypes(PgFieldTypeEnum.getAllFieldType());
                break;
            case "TiDB":
                storageDomain.setFieldTypes(TiDBFieldTypeEnum.getAllFieldType());
                break;
            case "Sap":
                storageDomain.setFieldTypes(SapFieldTypeEnum.getAllFieldType());
                break;
            case "KINGBASE8":
                storageDomain.setFieldTypes(Kingbase8FieldTypeEnum.getAllFieldType());
                break;
            case "DM":
                storageDomain.setFieldTypes(Kingbase8FieldTypeEnum.getAllFieldType());
                break;
            default:
                throw new BusinessException("不支持的存储类型");
        }
        return storageDomain;
    }

    /**
     * 页面内部使用, 删除了顶层的模型
     *
     * @param projectId
     * @return
     */
    @Override
    public List<ThemeDomain> getThemesByProjectId(Long projectId, String modelType) {
        List<ThemeDomain> themeDomains = getThemeDomains(projectId, modelType);
        // 4类模型,总线矩阵左侧主题目录都把最顶层主题去掉
        themeDomains.removeIf(themeDomain -> Objects.equals(0L, themeDomain.getPid()));
        return themeDomains;
    }

    /**
     * 直接调用主题Service的方法
     *
     * @param projectId
     * @return
     */
    @Override
    public List<ThemeDomain> getMenuByProjectId(Long projectId, String modelType) {
        List<ThemeDomain> themeDomains = getThemeDomains(projectId, modelType);
        // 设置主菜单
        themeDomains.stream()
            .filter(themeDomain -> "0".equals(themeDomain.getPid()))
            .forEach(themeDomain -> themeDomain.setThemeName("全部分组"));
        return themeDomains;
    }

    /**
     * 通过模型id列表查询表信息
     *
     * @param modelIds
     * @return
     */
    @Override
    public Map<Long, TblPropEntity> getModelIdTblMap(List<Long> modelIds) {
        Long projectId = ServletUtils.getProjectId();
        List<TblPropEntity> tblPropEntityList =
            tblPropService.getTblPropListByIdList(projectId, modelIds);
        return tblPropEntityList.stream()
            .collect(Collectors.toMap(TblPropEntity::getId, Function.identity()));
    }

    @NotNull
    private PageResult<ModelListDomain> getModelListDomainPageResult(
        PageResult<TableInfoBizDomain> tablePage) {
        PageResult<ModelListDomain> pageResult =
            new PageResult(
                Lists.newArrayList(),
                tablePage.getPageNum(),
                tablePage.getPageSize(),
                tablePage.getTotalElements());
        List<TableInfoBizDomain> result = tablePage.getResult();
        List<ModelListDomain> modelListDomainList = new LinkedList<>();
        for (TableInfoBizDomain tableInfoBizDomain : result) {
            ModelListDomain modelListDomain = new ModelListDomain();
            modelListDomain.setId(tableInfoBizDomain.getId());
            modelListDomain.setTblId(tableInfoBizDomain.getTblId());
            modelListDomain.setCnName(tableInfoBizDomain.getTableComment());
            modelListDomain.setEnName(tableInfoBizDomain.getTableName());
            modelListDomain.setSchema(tableInfoBizDomain.getSchema());
            modelListDomain.setTableType(
                null != tableInfoBizDomain.getTblType() ? tableInfoBizDomain.getTblType().name() : "");
            modelListDomain.setCreateBy(tableInfoBizDomain.getCreatorUsername());
            // pg last ddl null
            modelListDomain.setUpdateTime(tableInfoBizDomain.getUpdateTime());
            modelListDomain.setShowTableSize(tableInfoBizDomain.getShowTableSize());
            modelListDomain.setModelType(tableInfoBizDomain.getModelType());

            modelListDomain.setDataSecretId(tableInfoBizDomain.getDataSecretId());

            modelListDomainList.add(modelListDomain);
        }
        pageResult.setResult(modelListDomainList);
        return pageResult;
    }

    @NotNull
    private List<ModelListDomain> getModelListDomainResult(List<TableInfoBizDomain> result) {
        List<ModelListDomain> modelListDomainList = new LinkedList<>();
        for (TableInfoBizDomain tableInfoBizDomain : result) {
            ModelListDomain modelListDomain = new ModelListDomain();
            modelListDomain.setId(tableInfoBizDomain.getId());
            modelListDomain.setTblId(tableInfoBizDomain.getTblId());
            modelListDomain.setCnName(tableInfoBizDomain.getTableComment());
            modelListDomain.setEnName(tableInfoBizDomain.getTableName());
            modelListDomain.setSchema(tableInfoBizDomain.getSchema());
            modelListDomain.setTableType(
                null != tableInfoBizDomain.getTblType() ? tableInfoBizDomain.getTblType().name() : "");
            modelListDomain.setCreateBy(tableInfoBizDomain.getCreatorUsername());
            // pg last ddl null
            modelListDomain.setUpdateTime(tableInfoBizDomain.getUpdateTime());
            modelListDomain.setShowTableSize(tableInfoBizDomain.getShowTableSize());
            modelListDomain.setModelType(tableInfoBizDomain.getModelType());

            modelListDomainList.add(modelListDomain);
        }
        return modelListDomainList;
    }

    @Nullable
    private boolean isEmptyTablePage(PageResult<TableInfoBizDomain> tablePage) {
        if (null == tablePage
            || null == tablePage.getPageNum()
            || null == tablePage.getPageSize()
            || null == tablePage.getTotalElements()
            || CollectionUtils.isEmpty(tablePage.getResult())) {
            return true;
        }
        return false;
    }

    private String getTblCnName(
        boolean mustReturnSchema,
        List<TableInfoDomain> tables,
        List<TableInfoDomain> views,
        TblPropEntity tblPropEntity) {
        if (TableInfoDomain.TblType.VIEW.name().equalsIgnoreCase(tblPropEntity.getTblType())
            && CollectionUtils.isNotEmpty(views)) {
            for (TableInfoDomain tableInfoDomain : views) {
                if (Objects.equals(tableInfoDomain.getTableName(), tblPropEntity.getTableName())) {
                    if (mustReturnSchema
                        && Objects.equals(tableInfoDomain.getSchema(), tblPropEntity.getSchema())) {
                        return tableInfoDomain.getTableComment();
                    }
                }
            }
        } else {
            if (CollectionUtils.isNotEmpty(tables)) {
                for (TableInfoDomain tableInfoDomain : tables) {
                    if (Objects.equals(tableInfoDomain.getTableName(), tblPropEntity.getTableName())) {
                        if (mustReturnSchema
                            && Objects.equals(tableInfoDomain.getSchema(), tblPropEntity.getSchema())) {
                            return tableInfoDomain.getTableComment();
                        }
                    }
                }
            }
        }
        return "";
    }

    /**
     * 项目中其他地方建表(OfflineSync, CustomForm), 新增模型
     *
     * @param externalSaveModelParam
     * @return
     */
    @Override
    public boolean externalSaveNewModel(ExternalSaveModelParam externalSaveModelParam) {
        // empty
        return true;
    }

    /**
     * 删除模型
     *
     * @param modelId
     * @return
     */
    @Override
    public boolean deleteModelById(Long modelId) {
        String userId = UserContext.getUserId();
        TblPropEntity tblPropEntity = tblPropService.getTblPropById(modelId);
        if (null == tblPropEntity) {
            log.error("非法参数模型id{}", modelId);
            return false;
        }
        // 判断modelId是否被关联到总线矩阵中
        ModelBindWarnDomain modelBindWarnDomain = dimFactService.getBindDimFact(modelId);
        if (null != modelBindWarnDomain) {
            String warnMsg =
                String.format(
                    "模型表已被关联到%s %s,不能删除", modelBindWarnDomain.getType(), modelBindWarnDomain.getName());
            throw new BusinessException(warnMsg);
        }

        Long projectId = ServletUtils.getProjectId();

        // 模型是否关联cdm
        List<TblRelationEntity> tblRelations =
            tblRelationService.listTblRelation(projectId, Arrays.asList(tblPropEntity.getTblId()));
        if (CollectionUtils.isNotEmpty(tblRelations)) {
            String warnMsg = String.format("模型表存在cdm表字段关联关系,不能删除");
            throw new BusinessException(warnMsg);
        }

        StorageEntity storageEntity = storageServiceImpl.tmpGetStorageInfoByProjectId(projectId);
        // 根据存储类型获取对应的表操作
        DSType dsType = DSType.from(storageEntity.getStorageType());
        TableOperation tableOperation = new TableOperation(dsType);
        final BaseDbManager dbManager = projectService.getDbManager(projectId);
        tableOperation.dropTable(
            storageEntity.getRmdbs().getDbName(),
            tblPropEntity.getSchema(),
            tblPropEntity.getTableName(),
            dbManager);
        tblPropEntity.setRowState(0);
        return tblPropService.updateTblPropById(tblPropEntity);
    }

    /**
     * 查询主题下, 查询多种类型的模型表
     *
     * @param themeId
     * @param type
     * @return
     */
    @Override
    public List<ModelIdNameDomain> getModelListByType(Long themeId, String type) {
        Long projectId = ServletUtils.getProjectId();
        List<TblPropEntity> matrixTblProp =
            tblPropService.getMatrixTblProp(projectId, themeId, type);
        return internalGetModelList(matrixTblProp);
    }

    /**
     * 组装模型表
     *
     * @param tblPropListByIdList
     * @return
     */
    private List<ModelIdNameDomain> internalGetModelList(List<TblPropEntity> tblPropListByIdList) {
        List<ModelIdNameDomain> resultList = new ArrayList<>();
        for (TblPropEntity entity : tblPropListByIdList) {
            ModelIdNameDomain modelIdNameDomain = new ModelIdNameDomain();
            modelIdNameDomain.setId(entity.getId());
            modelIdNameDomain.setEnName(entity.getTableName());
            resultList.add(modelIdNameDomain);
        }
        return resultList;
    }

    /**
     * 删除单个模型字段
     *
     * @param modelId
     * @param fieldName
     */
    @Deprecated
    public void deleteField(String modelId, String fieldName) {
    }

    /**
     * 新增单个模型字段
     *
     * @param saveFieldParam
     */
    @Deprecated
    @Override
    public void saveNewField(SaveFieldParam saveFieldParam) {
    }

    @Override
    public List<TblPropEntity> getAllModelByThemeId(Long themeId) {
        return tblPropService.getTblPropByThemeId(themeId);
    }

    @Override
    public List<ModelAllFieldsDomain> getAllModelByThemeIdList(List<Long> themeIdList) {
        List<TblPropEntity> modelEntities = tblPropService.getTblPropByThemeIdList(themeIdList);
        List<ModelAllFieldsDomain> modelListDomains = new LinkedList<>();
        for (TblPropEntity tblPropEntity : modelEntities) {
            ModelAllFieldsDomain modelAllFieldsDomain = new ModelAllFieldsDomain();
            modelAllFieldsDomain.setThemeId(tblPropEntity.getThemeId());
            modelAllFieldsDomain.setCnName(tblPropEntity.getTableComment());
            // TODO
            modelAllFieldsDomain.setSupportRtf(true);
            modelAllFieldsDomain.setTblPropId(tblPropEntity.getId());
            modelAllFieldsDomain.setModelType(
                Objects.requireNonNull(ModelTypeEnum.getModelByTableName(tblPropEntity.getTableName()))
                    .name());
            modelAllFieldsDomain.setRowState(tblPropEntity.getRowState());
            modelListDomains.add(modelAllFieldsDomain);
        }
        return modelListDomains;
    }

    //    @Override
    //    public List<DataStandardTblRelationDomain> getRelationByForeignTblId(Long projectId, Long
    // tblId) {
    //        TblPropEntity tblProp = tblPropService.getTblPropByTblId(projectId, tblId);
    //        DimFactEntity dimFact = dimFactService.getByModelId(tblProp.getId());
    //
    //        List<MatrixEntity> matrixs = busMatrixService.getMatrixByFactId(dimFact.getId());
    //        ArrayList<DataStandardTblRelationDomain> domains = Lists.newArrayList();
    //        for (MatrixEntity matrix : matrixs) {
    //            Long dimId = matrix.getDimId();
    //            DimFactEntity byPrimaryKey = dimFactService.getByPrimaryKey(dimId);
    //            if (Objects.isNull(byPrimaryKey.getTblPropId())) {
    //                continue;
    //            }
    //            TblPropEntity dimProp = tblPropService.getTblPropById(byPrimaryKey.getTblPropId());
    //            matrix
    //                    .getRelFields()
    //                    .forEach(
    //                            i -> {
    //                                String factField = i.getFactField();
    //                                String dimField = i.getDimField();
    //                                DataStandardTblRelationDomain domain =
    //                                        DataStandardTblRelationDomain.builder()
    //                                                .foreignTableField(factField)
    //                                                .mainTableField(dimField)
    //                                                .foreignTableId(tblId)
    //                                                .mainTableId(dimProp.getTblId())
    //                                                .build();
    //                                domains.add(domain);
    //                            });
    //        }
    //
    //        return domains;
    //    }

    /**
     * 外部同步来的tblProp没有model
     *
     * @param projectId
     * @param tblPropId
     * @return
     */
    @Override
    public ModelDomain getTblPropById(Long projectId, Long tblPropId) {
        TblPropEntity tblPropEntity = tblPropService.getTblPropById(tblPropId);
        if (null == tblPropEntity) {
            throw new BusinessException("检查模型参数");
        }
        ModelDomain modelDomain = new ModelDomain();
        modelDomain.setId(tblPropEntity.getId());
        //
        modelDomain.setEnName(tblPropEntity.getTableName());
        modelDomain.setThemeId(tblPropEntity.getThemeId());
        //    modelDomain.setLifeCycle(tblPropEntity.getLifecycle());
        modelDomain.setLifeCycle(-1);
        // TODO
        //    modelDomain.setSupportRtf(false);
        modelDomain.setCreateBy(tblPropEntity.getCreatorId());
        modelDomain.setUpdateTime(tblPropEntity.getUpdateTime());
        modelDomain.setCreateTime(tblPropEntity.getCreateTime());

        StorageEntity storageEntity = storageServiceImpl.tmpGetStorageInfoByProjectId(projectId);
        // 根据存储类型获取对应的表操作
        DSType dsType = DSType.from(storageEntity.getStorageType());
        TableOperation tableOperation = new TableOperation(dsType);
        final BaseDbManager dbManager = projectService.getDbManager(projectId);

        // 返回给前端判断是否展示分区|必填|富文本
        modelDomain.setHive(DSType.Hive == dsType);
        String schemaName = storageEntity.getRmdbs().getSchema();
        // sap 会同步过来其他schema的
        if (DSType.Sap == dsType) {
            schemaName = tblPropEntity.getSchema();
        }
        TableInfoDomain tableInfoDomain =
            tableOperation.getTableInfoDomain(
                storageEntity.getRmdbs().getDbName(),
                schemaName,
                tblPropEntity.getTableName(),
                dbManager);
        if (null == tableInfoDomain) {
            throw new BusinessException(
                String.format(
                    "检查数据库中%s是否存在",
                    TableInfoDomain.TblType.VIEW.name().equalsIgnoreCase(tblPropEntity.getTblType())
                        ? "视图"
                        : "表"));
        }
        modelDomain.setCnName(tableInfoDomain.getTableComment());
        // 从meatadata中取
        List<ModelDomain.Field> tableField =
            tableOperation.getTableField(
                storageEntity.getRmdbs().getDbName(),
                schemaName,
                tblPropEntity.getTableName(),
                dbManager);
        // 关闭连接
        closeDbManager(dbManager);
        modelDomain.setFields(tableField);

        return modelDomain;
    }

    /**
     * sap return schemas
     *
     * @param projectId
     * @return
     */
    @Override
    public List<String> getSchemas(Long projectId) {
        StorageEntity storageEntity = storageServiceImpl.tmpGetStorageInfoByProjectId(projectId);
        if (null == storageEntity) {
            throw new BusinessException("该项目下没有存储信息");
        }
        String storageType = storageEntity.getStorageType();
        if (DSType.Sap == DSType.valueOf(storageType)) {
            final BaseDbManager dbManager = projectService.getDbManager(projectId);
            List<String> schemaList = null;
            try {
                schemaList = dbManager.getSchemaList();
                schemaList =
                    schemaList.stream()
                        .filter(i -> !i.equalsIgnoreCase("system"))
                        .collect(Collectors.toList());
                return schemaList;
            } catch (SQLException e) {
                throw new BusinessException("检查sap数据库是否正常工作");

            } finally {
                if (null != dbManager) {
                    dbManager.close();
                }
            }
        }

        return Lists.newArrayList();
    }

    @Override
    public List<ModelListDomain> searchTable(String tableName) {
        String schema = null;
        if (tableName.contains(".")) {
            schema = tableName.split("\\.", 2)[0];
            tableName = tableName.split("\\.", 2)[1];
        }

        QueryTableParam queryTableParam = new QueryTableParam();
        queryTableParam.setSearchStr(tableName);
        queryTableParam.setSortField("UPDATETIME");
        queryTableParam.setOrder("desc");
        queryTableParam.setSchema(schema);

        var projectId = ServletUtils.getProjectId();

        final List<TableInfoBizDomain> tableList =
            tableService.getTableList(projectId, queryTableParam);

        if (CollectionUtils.isEmpty(tableList)) {
            return null;
        }

        List<ModelListDomain> domains = new ArrayList<>();

        for (TableInfoBizDomain tableInfoBizDomain : tableList) {
            ModelListDomain domain = new ModelListDomain();
            domain.setSchema(tableInfoBizDomain.getSchema());
            domain.setEnName(tableInfoBizDomain.getTableName());
            domain.setTblId(tableInfoBizDomain.getTblId());
            domains.add(domain);
        }

        return domains;
    }

    /**
     * 获取数据表密级ID
     *
     * @param modelId
     * @return
     */
    @Override
    public Long getTableDataSecretLevelByModelId(Long modelId) {
        TblPropEntity tblPropEntity = tblPropService.getTblPropById(modelId);
        return Optional.ofNullable(tblPropEntity).map(TblPropEntity::getDataSecretId).orElse(null);
    }

    /**
     * 更新mongo中的 model 和 tbl_prop 表
     *
     * @param saveModelParam
     * @param dbModel
     * @param tblPropEntity
     */
    private void updateTblProp(
        SaveModelParam saveModelParam, ModelDomain dbModel, TblPropEntity tblPropEntity) {

        // 比较修改的内容
        // 表名是否修改了
        String newTableName = saveModelParam.getEnName();
        String newCnName = saveModelParam.getCnName();
        Long newThemeId = saveModelParam.getThemeId();
        Integer newLifeCycle = saveModelParam.getLifeCycle();
        Long newDataSecretId = saveModelParam.getDataSecretId();
        String userId = UserContext.getUserId();

        SaveModelArg saveModelArg = saveModelArgMapper.map(dbModel);
//            ConvertUtil.copyProperties(dbModel, SaveModelArg.class);

        // 表名改变
        if (!Objects.equals(dbModel.getEnName(), newTableName)) {
            // 更新tblProperty doc 表名
            tblPropEntity.setTableName(newTableName);
        }
        if (!Objects.equals(dbModel.getLifeCycle(), newLifeCycle)) {
            // 更新tblProperty doc 生命周期
            tblPropEntity.setLifecycle(newLifeCycle);
        }
        if (!Objects.equals(dbModel.getCnName(), newCnName)) {
            // 更新model doc 表名
            tblPropEntity.setTableComment(newCnName);
        }
        if (!Objects.equals(dbModel.getThemeId(), newThemeId)) {
            // 更新model doc
            tblPropEntity.setThemeId(newThemeId);
        }
        if (!Objects.equals(dbModel.getDataSecretId(), newDataSecretId)) {
            tblPropEntity.setDataSecretId(newDataSecretId);
        }
        tblPropEntity.setUpdaterId(userId);
        tblPropEntity.setUpdateTime(new Date());
        tblPropService.updateTblPropById(tblPropEntity);
    }

    // 字段解析结果
    @Data
    @Builder
    public static class FieldsParseResult {
        private List<SaveModelParam.Field> newFieldList;
        private List<SaveModelParam.Field> updateFieldList;
        private List<SaveModelParam.Field> deleteFieldList;
    }

    /**
     * 关闭数据库连接
     *
     * @param dbManager
     */
    private void closeDbManager(BaseDbManager dbManager) {
        if (null != dbManager) {
            dbManager.close();
        }
    }

    /**
     * 从配置文件中获取PgManager
     *
     * @return
     */

    /**
     * @return
     */
    private boolean mustReturnSchema() {
        Long projectId = ServletUtils.getProjectId();
        StorageEntity storageEntity =
            storageServiceImpl.getStorageInfoByProjectId(projectId);
        if (null == storageEntity) throw new BusinessException("底层存储状态异常");
        return Database.SAP == Database.from(storageEntity.getStorageType());
    }

    /**
     * convert
     *
     * @param projectId
     * @param tblPropEntity
     * @return
     */
    private ModelDomain convertTblPropEntityToModelDomain(
        Long projectId, TblPropEntity tblPropEntity) {
        if (null == tblPropEntity) {
            throw new BusinessException("检查模型参数");
        }
        ModelDomain modelDomain = new ModelDomain();
        modelDomain.setId(tblPropEntity.getId());
        modelDomain.setCnName(tblPropEntity.getTableComment());
        modelDomain.setCreateBy(tblPropEntity.getCreatorId());
        modelDomain.setUpdateTime(tblPropEntity.getUpdateTime());
        modelDomain.setCreateTime(tblPropEntity.getCreateTime());
        modelDomain.setEnName(tblPropEntity.getTableName());
        modelDomain.setLifeCycle(tblPropEntity.getLifecycle());
        modelDomain.setThemeId(tblPropEntity.getThemeId());
        StorageEntity storageEntity = storageServiceImpl.tmpGetStorageInfoByProjectId(projectId);
        if (null == storageEntity) {
            throw new BusinessException("检查空间存储");
        }
        // 根据存储类型获取对应的表操作
        DSType dsType = DSType.from(storageEntity.getStorageType());
        TableOperation tableOperation = new TableOperation(dsType);
        final BaseDbManager dbManager = projectService.getDbManager(projectId);
        // 返回给前端判断是否展示分区|必填|富文本
        modelDomain.setHive(DSType.Hive == dsType);

        String schemaName = tblPropEntity.getSchema();
        try {
            // 从meatadata中取
            List<ModelDomain.Field> tableField =
                tableOperation.getTableField(
                    storageEntity.getRmdbs().getDbName(),
                    schemaName,
                    tblPropEntity.getTableName(),
                    dbManager);
            // 关闭连接
            modelDomain.setFields(tableField);
        } finally {
            closeDbManager(dbManager);
        }

        return modelDomain;
    }

    /**
     * @param cs
     * @return
     */
    private static boolean isAllAlphaLowerCase(CharSequence cs) {
        if (isEmpty(cs)) {
            return false;
        } else {
            int sz = cs.length();

            for (int i = 0; i < sz; ++i) {
                char c = cs.charAt(i);
                if (Character.isAlphabetic(c) && !Character.isLowerCase(c)) {
                    return false;
                }
            }

            return true;
        }
    }

    /**
     * 获取主题
     *
     * @param projectId
     * @param modelType
     * @return
     */
    private List<ThemeDomain> getThemeDomains(Long projectId, String modelType) {
        String themeName = "";
        switch (modelType) {
            case "ods":
                themeName = "贴源层";
                break;
            case "public":
                themeName = "公共层";
                break;
            case "apply":
                themeName = "应用层";
                break;
            case "other":
                themeName = "其他层";
                break;
            default:
                throw new BusinessException("模型类型不正确");
        }
        return themeService.treeThemes(projectId, themeName);
    }
}
